package com.drakulo.games.ais.ui.state;

import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;

import com.drakulo.games.ais.AloneInSpace;
import com.drakulo.games.ais.core.GameData;
import com.drakulo.games.ais.core.Settings;
import com.drakulo.games.ais.ui.FontHelper;
import com.drakulo.games.ais.ui.I18n;
import com.drakulo.games.ais.ui.UIHelper;
import com.drakulo.games.ais.ui.component.ActionHandler;
import com.drakulo.games.ais.ui.component.Bindable;
import com.drakulo.games.ais.ui.component.UIComponent;
import com.drakulo.games.ais.ui.component.button.Button;
import com.drakulo.games.ais.ui.component.button.TextButton;
import com.drakulo.games.ais.ui.component.window.Window;
import com.drakulo.games.ais.ui.twlbridge.BasicTWLGameState;

import de.matthiasmann.twl.Widget;

/**
 * This class is a global wrapper for all in-game states. It renders the basic
 * top bar with menu and handle game engine updates.
 * 
 * @author Drakulo
 * 
 */
public abstract class GameState extends BasicTWLGameState {
	/** The top zone height */
	public static final int TOP_BAR_HEIGHT = 36;
	/** The game menu window height when the menu is not shown */
	private static final int MENU_SMALL_HEIGHT = 46;
	/** The game menu window height when the menu is shown */
	private static final int MENU_FULL_HEIGHT = 195;

	/** The state ID */
	private int stateId;
	/** The components of the state */
	private List<UIComponent> components;
	/** The game menu buttons */
	private List<Button> gameMenuButtons;
	/** The mouse last X coordinate */
	//private int mx;
	/** The mouse Y last coordinate */
	//private int my;
	/** The game menu background */
	private Window gameMenuWindow;

	/** componentsToRemove */
	private List<UIComponent> componentsToRemove;

	/**
	 * Constructor
	 * 
	 * @param id
	 *            - the state ID
	 */
	public GameState(int id) {
		stateId = id;
		components = new ArrayList<UIComponent>();
		gameMenuButtons = new ArrayList<Button>();
		componentsToRemove = new ArrayList<UIComponent>();
	}

	@Override
	public void init(GameContainer gc, StateBasedGame game)
			throws SlickException {
		GameData.setGame(game);
		GameData.setGameContainer(gc);
		createGameMenu();

		initState();
	}

	@Override
	public void render(GameContainer gc, StateBasedGame game, Graphics g)
			throws SlickException {
		renderState(g);

		// Top zone
		UIHelper.drawDarkBox(g, 0, 0, Settings.WIDTH, TOP_BAR_HEIGHT + 5);

		// Render the top zone data
		renderTopZone(g);

		gameMenuWindow.render(g);

		// Render the components
		for (UIComponent c : components) {
			c.render(g);
		}

		// String mouseData = "[" + mx + ", " + my + "]";
		// Font font = FontHelper.getDefaultFont();
		// font.drawString(0, 0, mouseData);
	}

	@Override
	public void update(GameContainer gc, StateBasedGame game,
			int timeSinceLastUpdate) throws SlickException {
		// Remove unused components
		safeRemove();

		final Input input = gc.getInput();
		// mx = input.getMouseX();
		// my = input.getMouseY();
		updateState(input, timeSinceLastUpdate);

		gameMenuWindow.update(input);

		// Update the components
		List<UIComponent> deletedComponents = new ArrayList<UIComponent>();
		for (UIComponent c : components) {
			if(c != null){
				c.update(input);								
			}else{
				deletedComponents.add(c);
			}
		}
		components.removeAll(deletedComponents);

		// Special handling for the game menu : if the game menu is shown and
		// the player move the mouse outside of the game menu, it is
		// automatically closed
		if (!gameMenuWindow.isHovered()
				&& gameMenuWindow.getHeight() == MENU_FULL_HEIGHT) {
			toggleGameMenu();
		}
	}

	@Override
	public int getID() {
		return stateId;
	}

	/**
	 * Adds a component to the handling process. By adding a component to the
	 * GameState, the given component will automatically be updated and
	 * rendered.
	 * 
	 * @param component
	 *            - the component to add
	 */
	public void add(UIComponent component) {
		components.add(component);
		if (component instanceof Bindable) {
			Bindable b = (Bindable) component;
			if (getRootPane().getChildIndex(b.getBindable()) == -1) {
				b.bindTo(getRootPane());
			}
		}
	}
	
	public void add(Widget w){
		if (getRootPane().getChildIndex(w) == -1) {
			getRootPane().add(w);
		}
	}

	public void remove(UIComponent comp) {
		componentsToRemove.add(comp);
	}

	public void removeAll(List<? extends UIComponent> list) {
		componentsToRemove.addAll(list);
	}

	private void safeRemove() {
		List<UIComponent> toRemove = new ArrayList<UIComponent>();
		for (UIComponent c : componentsToRemove) {
			components.remove(c);
			toRemove.add(c);
			if (c instanceof Bindable) {
				Bindable b = (Bindable) c;
				getRootPane().removeChild(b.getBindable());
			}
		}
		components.removeAll(toRemove);
	}

	/**
	 * Create the game menu
	 */
	private void createGameMenu() {
		final int screenPadding = 10;
		final int buttonWidth = 120;
		final int buttonHeight = 26;
		final int vPadding = 30;
		int x = screenPadding + 1;
		int y = screenPadding;
		String label;

		// Button for open the game menu
		label = FontHelper.firstToUpper(I18n.get("global.game_menu"));
		TextButton gameMenuButton = new TextButton(label, x, y);
		gameMenuButton.setSize(buttonWidth, buttonHeight);
		gameMenuButton.setActionHandler(new ActionHandler() {

			@Override
			public void run() {
				toggleGameMenu();
			}
		});
		add(gameMenuButton);

		// Load button
		y += vPadding;
		label = FontHelper.firstToUpper(I18n.get("global.load"));
		Button loadButton = new TextButton(label, x, y);
		loadButton.setSize(buttonWidth, buttonHeight);
		loadButton.disable();
		loadButton.hide();
		loadButton.setActionHandler(new ActionHandler() {

			@Override
			public void run() {
				// TODO
			}
		});
		gameMenuButtons.add(loadButton);
		add(loadButton);

		// Save button
		y += vPadding;
		label = FontHelper.firstToUpper(I18n.get("global.save"));
		Button saveButton = new TextButton(label, x, y);
		saveButton.hide();
		saveButton.setSize(buttonWidth, buttonHeight);
		saveButton.disable();
		saveButton.setActionHandler(new ActionHandler() {

			@Override
			public void run() {
				// TODO
			}
		});
		gameMenuButtons.add(saveButton);
		add(saveButton);

		// Settings button
		y += vPadding;
		label = FontHelper.firstToUpper(I18n.get("global.settings"));
		Button settingsButton = new TextButton(label, x, y);
		settingsButton.setSize(buttonWidth, buttonHeight);
		settingsButton.disable();
		settingsButton.hide();
		settingsButton.setActionHandler(new ActionHandler() {

			@Override
			public void run() {
				// TODO
			}
		});
		gameMenuButtons.add(settingsButton);
		add(settingsButton);

		// Main menu button
		y += vPadding;
		label = FontHelper.firstToUpper(I18n.get("global.main_menu"));
		Button mainMenu = new TextButton(label, x, y);
		mainMenu.setSize(buttonWidth, buttonHeight);
		mainMenu.hide();
		mainMenu.setActionHandler(new ActionHandler() {

			@Override
			public void run() {
				GameData.getGame().enterState(AloneInSpace.MAIN_MENU_STATE);
			}
		});
		gameMenuButtons.add(mainMenu);
		add(mainMenu);
		
		// Exit button
		y += vPadding;
		label = FontHelper.firstToUpper(I18n.get("global.exit"));
		Button exitButton = new TextButton(label, x, y);
		exitButton.setSize(buttonWidth, buttonHeight);
		exitButton.hide();
		exitButton.setActionHandler(new ActionHandler() {

			@Override
			public void run() {
				GameData.getGameContainer().exit();
			}
		});
		gameMenuButtons.add(exitButton);
		add(exitButton);

		// The game menu background
		gameMenuWindow = new Window(0, 0, buttonWidth + 20, MENU_SMALL_HEIGHT);
		gameMenuWindow.show();
	}

	protected void toggleGameMenu() {
		for (Button b : gameMenuButtons) {
			b.toggleDisplay();
		}
		if (gameMenuWindow.getHeight() == MENU_SMALL_HEIGHT) {
			gameMenuWindow.setHeight(MENU_FULL_HEIGHT);
		} else {
			gameMenuWindow.setHeight(MENU_SMALL_HEIGHT);
		}
	}

	/**
	 * Initialize the state
	 * 
	 * @throws SlickException
	 */
	protected abstract void initState() throws SlickException;

	/**
	 * Render the state
	 * 
	 * @param g
	 *            - the graphic canvas
	 * @throws SlickException
	 */
	protected abstract void renderState(Graphics g) throws SlickException;

	/**
	 * Update the state
	 * 
	 * @param input
	 *            - the player input
	 * @param timeSinceLastUpdate
	 *            - the time elapsed since the last update call
	 * @throws SlickException
	 */
	protected abstract void updateState(Input input, int timeSinceLastUpdate)
			throws SlickException;

	/**
	 * Render the state specifi data in the top area
	 * 
	 * @param g
	 *            - the graphic canvas
	 * @throws SlickException
	 */
	protected void renderTopZone(Graphics g) throws SlickException {
		// Render nothing by default. Must be overridden in subclass
	}
}
